package com.ruoyi.system.monitor;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.system.domain.MonitorConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.function.Function;

@Slf4j
public class MonitorDataSourceHelper {

    private static final Map<String, DataSourceMeta> sourceMap = new HashMap<>();

    public static DataSource getDatasource(MonitorConfig config, Function<MonitorConfig, DataSource> createDataSource) {
        MonitorParams params = JSON.parseObject(config.getParams(), MonitorParams.class);
        String key = params.toString();
        DataSourceMeta meta = sourceMap.get(key);
        if (meta != null) {
            meta.lastUsedTime = LocalDateTime.now();
            return meta.dataSource;
        }
        DataSource source = createDataSource.apply(config);
        meta = new DataSourceMeta(source, LocalDateTime.now());
        sourceMap.put(key, meta);
        startTimer();
        return meta.dataSource;
    }

    @AllArgsConstructor
    public static class DataSourceMeta {

        private DataSource dataSource;

        private LocalDateTime lastUsedTime;
    }

    private static Timer timer = null;
    private static final long MAX_IDLE_TIME = 5 * 60 * 1000; // 30 minutes in milliseconds
    private static final long TIMER_IDLE_TIME = 60 * 1000; // 1 minutes in milliseconds


    private synchronized static void startTimer() {
        if (timer != null) {
            return;
        }
        timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                log.info("【数据源检查】");
                for (Map.Entry<String, DataSourceMeta> entry : sourceMap.entrySet()) {
                    // 超过最大等待时间
                    if (entry.getValue().lastUsedTime.until(LocalDateTime.now(), ChronoUnit.MILLIS) > MAX_IDLE_TIME) {
                        log.error("【移除数据源】{}", entry.getKey());
                        removeDataSource(entry.getValue().dataSource);
                        sourceMap.remove(entry.getKey());
                    } else {
                        ping(entry.getValue().dataSource);
                    }
                }
            }
        };
        timer.scheduleAtFixedRate(task, 0, TIMER_IDLE_TIME);
    }

    private static void ping(DataSource dataSource) {
        try {
            String sql = "SELECT 1";
            dataSource.getConnection().createStatement().execute(sql);
            log.info("【数据源维持心跳】成功：{}", sql);
        } catch (SQLException e) {
            log.error("【数据源维持心跳】失败", e);
        }
    }

    private static void removeDataSource(DataSource dataSource) {
        if (dataSource instanceof HikariDataSource) {
            ((HikariDataSource) dataSource).close();
        }
    }
}
